<!DOCTYPE html>
<html lang="en">
<head>
    <title>半透明</title>
    <meta charset="utf-8">

    <script id="translucency-vert" type="x-shader/x-vertex">
			void main() {
				gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
			}
	</script>

    <script id="translucency-frag" type="x-shader/x-fragment">
            precision mediump float;

            vec4 packDepth(float depth){
                vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);
                vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);
                vec4 rgbaDepth = fract(depth * bitShift);
                rgbaDepth -= rgbaDepth.gbaa * bitMask;
                return rgbaDepth;
            }

            void main(){
                gl_FragColor = packDepth(gl_FragCoord.z);
            }
    </script>

    <script id="post-vert" type="x-shader/x-vertex">
			varying vec2 vUv;
			void main() {
				vUv = uv;
				gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
			}
	</script>

    <script id="post-frag" type="x-shader/x-fragment">
            precision mediump float;

			varying vec2 vUv;
			uniform sampler2D tDepthFront;
			uniform sampler2D tDepthBack;
			uniform float cameraNear;
			uniform float cameraFar;

            float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {
                return ( near * far ) / ( ( far - near ) * invClipZ - far );
            }

            float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {
                return ( viewZ + near ) / ( near - far );
            }

            float unpackDepth(const in vec4 rgbaDepth) {
                const vec4 bitShift = vec4(1.0, 1.0/255.0, 1.0/(255.0 * 255.0), 1.0/(255.0*255.0*255.0));
                float depth = dot(rgbaDepth, bitShift);
                return depth;
            }

			float readDepth( sampler2D depthSampler, vec2 coord ) {
				float fragCoordZ = unpackDepth(texture2D( depthSampler, coord ));
				float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
				return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
			}

			void main() {
				float depthFront = readDepth( tDepthFront, vUv );
				float depthBack = readDepth( tDepthBack, vUv );
				float thickness = depthBack - depthFront;
				float light = 1.0 - pow((thickness - 1.0), 4.0);
				if(depthFront == depthBack){
                    light = 0.0;
				}
				float r = light/1.2;
				float g = light/1.2;
				float b = light;
				float a = 1.0;
				gl_FragColor = vec4(r, g, b, a);
			}
	</script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>

    <script src="./js/TrackballControls.js"></script>

</head>
<body>
<canvas></canvas>
<script type="module">
    let camera, scene, renderer, controls;
    let targetFront, targetBack;
    let postScene, postCamera;
    let supportsExtension = true;

    init();
    animate();

    function init() {

        renderer = new THREE.WebGLRenderer( {
            canvas: document.querySelector( 'canvas' ),
            antialias:true
        } );

        if ( ! renderer.extensions.get( 'WEBGL_depth_texture' ) ) {

            supportsExtension = false;
            document.querySelector( '#error' ).style.display = 'block';
            return;

        }

        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );

        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 1000);
        camera.position.z = 500;

        controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.rotateSpeed = 1.6;// 旋转速度
        controls.zoomSpeed = 1.6;// 缩放速度
        controls.panSpeed = 0.6;// 平controls
        controls.staticMoving = true;// 静止移动，为 true 则没有惯性
        controls.dynamicDampingFactor = 0.2;// 阻尼系数 越小 则滑动越大
        controls.minDistance = 5; // 最小视角
        controls.maxDistance = 1000;// 最大视角 Infinity 无穷大

        // Create a multi render target with Float buffers
        targetFront = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
        targetBack = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );

        scene = new THREE.Scene();
    }

    function setupScene(frontOrBack) {
        //clear
        if(scene.children.length !== 0){
            scene.children[0].geometry.dispose();
            scene.children[0].material.dispose();
            scene.remove(scene.children[0]);
        }
        //add new
        let geometry = new THREE.CubeGeometry(100, 100, 100, 10, 10, 10);
        let material = new THREE.ShaderMaterial({
            vertexShader: document.querySelector( '#translucency-vert' ).textContent.trim(),
            fragmentShader: document.querySelector( '#translucency-frag' ).textContent.trim()
        });
        if(frontOrBack === "front"){
            material.side = THREE.FrontSide;
        }else if(frontOrBack === "back"){
            material.side = THREE.BackSide;
        }
        let mesh = new THREE.Mesh( geometry, material );
        scene.add( mesh );
    }

    function setupPost() {
        // Setup post processing stage
        postCamera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
        let postMaterial = new THREE.ShaderMaterial( {
            vertexShader: document.querySelector( '#post-vert' ).textContent.trim(),
            fragmentShader: document.querySelector( '#post-frag' ).textContent.trim(),
            uniforms: {
                cameraNear: { value: camera.near },
                cameraFar: { value: camera.far },
                tDepthFront: { value: targetFront.texture },
                tDepthBack: { value: targetBack.texture }
            }
        } );
        let postPlane = new THREE.PlaneBufferGeometry( 2, 2 );
        let postQuad = new THREE.Mesh( postPlane, postMaterial );
        postScene = new THREE.Scene();
        postScene.add( postQuad );
    }

    function animate() {
        if ( ! supportsExtension ) return;
        requestAnimationFrame( animate );
        controls.update();
        //前表面深度图
        renderer.setRenderTarget( targetFront );
        setupScene("front");
        renderer.render( scene, camera );
        //后表面深度图
        renderer.setRenderTarget( targetBack );
        setupScene("back");
        renderer.render( scene, camera );
        // render post FX
        renderer.setRenderTarget( null );
        setupPost();
        renderer.render( postScene, postCamera );
    }
</script>
</body>
</html>